package com.starry.module.system.core.oauth2.authorization.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.starry.core.common.constants.CommonConstant;
import com.starry.core.common.enums.UserTypeEnum;
import com.starry.core.common.execption.ServiceException;
import com.starry.core.common.tenant.context.TenantContextHolder;
import com.starry.core.security.context.SecurityInfoContext;
import com.starry.core.security.domain.BaseUser;
import com.starry.module.system.core.member.entity.MemberUser;
import com.starry.module.system.core.member.service.MemberUserService;
import com.starry.module.system.core.menu.service.SysMenuService;
import com.starry.module.system.core.oauth2.authorization.constant.SecurityConstants;
import com.starry.module.system.core.oauth2.authorization.domain.CustomGrantedAuthority;
import com.starry.module.system.core.oauth2.authorization.domain.LoginUser;
import com.starry.module.system.core.oauth2.entity.Oauth2ThirdAccount;
import com.starry.module.system.core.oauth2.service.Oauth2ThirdAccountService;
import com.starry.module.system.core.tenant.service.SysTenantService;
import com.starry.module.system.core.user.entity.SysUser;
import com.starry.module.system.core.user.service.SysUserService;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.Set;
import java.util.stream.Collectors;

import static com.starry.module.system.core.enums.ErrorCodeConstants.REQUEST_TENANT_NOT_EXISTS;

/**
 * Oauth2用户信息服务实现类
 *
 * @Author xiaoke
 */
@Service
public class Oauth2UserDetailsService implements UserDetailsService {

    private final SysUserService sysUserService;
    private final SysMenuService sysMenuService;
    private final HttpServletRequest request;
    private final SysTenantService sysTenantService;
    private final MemberUserService memberUserService;
    private final Oauth2ThirdAccountService oauth2ThirdAccountService;

    public Oauth2UserDetailsService(SysUserService sysUserService, SysMenuService sysMenuService, HttpServletRequest request, SysTenantService sysTenantService, MemberUserService memberUserService, Oauth2ThirdAccountService oauth2ThirdAccountService) {
        this.sysUserService = sysUserService;
        this.sysMenuService = sysMenuService;
        this.request = request;
        this.sysTenantService = sysTenantService;
        this.memberUserService = memberUserService;
        this.oauth2ThirdAccountService = oauth2ThirdAccountService;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        // 登录类型
        String loginType = request.getParameter(SecurityConstants.LOGIN_TYPE_NAME);

        if (loginType == null) {
            throw new ServiceException("登录类型不能为空");
        }

        Oauth2ThirdAccount oauth2ThirdAccount = null;

        // NONE_PROVIDED 表示 null
        if (!username.equals("NONE_PROVIDED")) {
            oauth2ThirdAccount = JSONUtil.toBean(username, Oauth2ThirdAccount.class);
        }

        String grantType = request.getParameter("grant_type");

        //内部登录
        //获取内部调用用户
        if (username.equals(CommonConstant.INTERNAL_CLIENT_CLIENT_SECRET)) {
            return internalUserDetails();
        }

        //设置租户Id
        setTenant();

        // 校验当前租户是否有效
        sysTenantService.validateTenant();

        if (oauth2ThirdAccount == null) {
            throw new ServiceException("第三方登录信息不能为空");
        }

        return switch (loginType) {
            case SecurityConstants.ADMIN_LOGIN -> adminLogin(oauth2ThirdAccount, grantType);
            case SecurityConstants.MEMBER_LOGIN -> memberLogin(oauth2ThirdAccount, grantType);
            default -> throw new ServiceException("登录类型不存在");
        };
    }

    /**
     * 设置租户Id
     * 1. 从请求头中获取租户Id
     * 2. 从请求头中获取域名，根据域名查询租户Id
     */
    private void setTenant() {
        Long tenantId = TenantContextHolder.getTenantId();
        if (tenantId != null) return;
        String origin = request.getHeader("origin");
        if (origin == null) {
            throw new ServiceException(REQUEST_TENANT_NOT_EXISTS);
        }
        tenantId = sysTenantService.getByWebSiteTenantId(origin);
        if (tenantId == null) {
            throw new ServiceException(REQUEST_TENANT_NOT_EXISTS);
        }
        TenantContextHolder.setTenantId(tenantId);
    }


    /**
     * 管理员登录
     *
     * @param thirdAccount 第三方账号信息
     * @param grantType    认证类型
     * @return UserDetails
     */
    private UserDetails adminLogin(Oauth2ThirdAccount thirdAccount, String grantType) {
        SysUser sysUser = null;

        thirdAccount.setUserType(UserTypeEnum.ADMIN.getValue());

        // type!=null 为第三方登陆
        if (thirdAccount.getType() != null) {
            sysUser = oauth2ThirdAccountService.getByOpenIdAndType(thirdAccount.getOpenid(), thirdAccount.getType());
            if (sysUser == null) {
                // 初始化第三方账号信息
                sysUser = oauth2ThirdAccountService.initThirdAccount(thirdAccount);
            }
        } else {
            sysUser = sysUserService.getByValue(thirdAccount.getOpenid());
            // 如果用户不存在，则初始化用户信息
            if (sysUser == null && SecurityConstants.GRANT_TYPE_SMS_CODE.equals(grantType)) {
                sysUser = sysUserService.initialUser(thirdAccount.getOpenid());
            }
        }
        if (sysUser == null) {
            throw new ServiceException("用户不存在");
        }
        if (sysUser.getStatus() == 0) {
            throw new ServiceException("用户已被禁用");
        }
        BaseUser user = BeanUtil.toBean(sysUser, BaseUser.class);
        // 后台用户
        user.setUserType(UserTypeEnum.ADMIN);

        Set<String> userPerms = sysUserService.getByIdUserPerms(user.getId());
        LoginUser loginUser = new LoginUser();
        loginUser.setUser(user);
        Set<CustomGrantedAuthority> collect = userPerms.stream()
                .map(CustomGrantedAuthority::new)
                .collect(Collectors.toSet());
        loginUser.setAuthorities(collect);

        SecurityInfoContext.setLoginUser(JSONUtil.toJsonStr(loginUser.getUser()));

        return loginUser;

    }


    /**
     * 会员登录
     *
     * @param thirdAccount 第三方账号信息
     * @param grantType    认证类型
     * @return UserDetails
     */
    private UserDetails memberLogin(Oauth2ThirdAccount thirdAccount, String grantType) {
        MemberUser memberUser;

        thirdAccount.setUserType(UserTypeEnum.MEMBER.getValue());

        // type!=null 为第三方登陆
        if (thirdAccount.getType() != null) {
            memberUser = oauth2ThirdAccountService.getMemberByOpenIdAndType(thirdAccount.getOpenid(), thirdAccount.getType());
            if (memberUser == null) {
                // 初始化第三方账号信息
                memberUser = oauth2ThirdAccountService.initMemberThirdAccount(thirdAccount);
            }
        } else {
            memberUser = memberUserService.getByValue(thirdAccount.getOpenid());
            if (memberUser == null && SecurityConstants.GRANT_TYPE_SMS_CODE.equals(grantType)) {
                memberUser = memberUserService.initialMemberUser(thirdAccount.getOpenid());
            }
        }
        if (memberUser == null) {
            throw new ServiceException("用户不存在");
        }

        if (memberUser.getStatus() == 0) {
            throw new ServiceException("用户已被禁用");
        }

        BaseUser user = BeanUtil.toBean(memberUser, BaseUser.class);
        // 后台用户
        user.setUserType(UserTypeEnum.MEMBER);
        LoginUser loginUser = new LoginUser();
        loginUser.setUser(user);

        SecurityInfoContext.setLoginUser(JSONUtil.toJsonStr(loginUser.getUser()));
        return loginUser;
    }


    /**
     * 内部调用用户信息
     *
     * @return UserDetails
     */
    private UserDetails internalUserDetails() {
        LoginUser loginUser = new LoginUser();
        BaseUser user = new BaseUser();
        user.setId(0L);
        user.setUsername("internal");
        Set<String> perms = sysMenuService.getAllPerms();
        Set<CustomGrantedAuthority> collect = perms.stream()
                .map(CustomGrantedAuthority::new)
                .collect(Collectors.toSet());
        loginUser.setAuthorities(collect);
        loginUser.setUser(user);
        return loginUser;
    }
}
